home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / lib / tk2.3 / dist / tkButton.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-08-24  |  44.9 KB  |  1,447 lines

  1. /* 
  2.  * tkButton.c --
  3.  *
  4.  *    This module implements a collection of button-like
  5.  *    widgets for the Tk toolkit.  The widgets implemented
  6.  *    include labels, buttons, check buttons, and radio
  7.  *    buttons.
  8.  *
  9.  * Copyright 1990-1992 Regents of the University of California.
  10.  * Permission to use, copy, modify, and distribute this
  11.  * software and its documentation for any purpose and without
  12.  * fee is hereby granted, provided that the above copyright
  13.  * notice appear in all copies.  The University of California
  14.  * makes no representations about the suitability of this
  15.  * software for any purpose.  It is provided "as is" without
  16.  * express or implied warranty.
  17.  */
  18.  
  19. #ifndef lint
  20. static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkButton.c,v 1.69 92/08/21 11:42:47 ouster Exp $ SPRITE (Berkeley)";
  21. #endif
  22.  
  23. #include "default.h"
  24. #include "tkConfig.h"
  25. #include "tkInt.h"
  26.  
  27. /*
  28.  * A data structure of the following type is kept for each
  29.  * widget managed by this file:
  30.  */
  31.  
  32. typedef struct {
  33.     Tk_Window tkwin;        /* Window that embodies the button.  NULL
  34.                  * means that the window has been destroyed. */
  35.     Tcl_Interp *interp;        /* Interpreter associated with button. */
  36.     int type;            /* Type of widget:  restricts operations
  37.                  * that may be performed on widget.  See
  38.                  * below for possible values. */
  39.  
  40.     /*
  41.      * Information about what's in the button.
  42.      */
  43.  
  44.     char *text;            /* Text to display in button (malloc'ed)
  45.                  * or NULL. */
  46.     int textLength;        /* # of characters in text. */
  47.     char *textVarName;        /* Name of variable (malloc'ed) or NULL.
  48.                  * If non-NULL, button displays the contents
  49.                  * of this variable. */
  50.     Pixmap bitmap;        /* Bitmap to display or None.  If not None
  51.                  * then text and textVar are ignored. */
  52.  
  53.     /*
  54.      * Information used when displaying widget:
  55.      */
  56.  
  57.     Tk_Uid state;        /* State of button for display purposes:
  58.                  * normal, active, or disabled. */
  59.     Tk_3DBorder normalBorder;    /* Structure used to draw 3-D
  60.                  * border and background when window
  61.                  * isn't active.  NULL means no such
  62.                  * border exists. */
  63.     Tk_3DBorder activeBorder;    /* Structure used to draw 3-D
  64.                  * border and background when window
  65.                  * is active.  NULL means no such
  66.                  * border exists. */
  67.     int borderWidth;        /* Width of border. */
  68.     int relief;            /* 3-d effect: TK_RELIEF_RAISED, etc. */
  69.     XFontStruct *fontPtr;    /* Information about text font, or NULL. */
  70.     XColor *normalFg;        /* Foreground color in normal mode. */
  71.     XColor *activeFg;        /* Foreground color in active mode.  NULL
  72.                  * means use normalFg instead. */
  73.     XColor *disabledFg;        /* Foreground color when disabled.  NULL
  74.                  * means use normalFg with a 50% stipple
  75.                  * instead. */
  76.     GC normalTextGC;        /* GC for drawing text in normal mode.  Also
  77.                  * used to copy from off-screen pixmap onto
  78.                  * screen. */
  79.     GC activeTextGC;        /* GC for drawing text in active mode (NULL
  80.                  * means use normalTextGC). */
  81.     Pixmap gray;        /* Pixmap for displaying disabled text if
  82.                  * disabledFg is NULL. */
  83.     GC disabledGC;        /* Used to produce disabled effect.  If
  84.                  * disabledFg isn't NULL, this GC is used to
  85.                  * draw button text or icon.  Otherwise
  86.                  * text or icon is drawn with normalGC and
  87.                  * this GC is used to stipple background
  88.                  * across it. */
  89.     int leftBearing;        /* Amount text sticks left from its origin,
  90.                  * in pixels. */
  91.     int rightBearing;        /* Amount text sticks right from its origin. */
  92.     int width, height;        /* If > 0, these specify dimensions to request
  93.                  * for window, in characters for text and in
  94.                  * pixels for bitmaps.  In this case the actual
  95.                  * size of the text string or bitmap is
  96.                  * ignored in computing desired window size. */
  97.     int padX, padY;        /* Extra space around text or bitmap (pixels
  98.                  * on each side). */
  99.     Tk_Anchor anchor;        /* Where text/bitmap should be displayed
  100.                  * inside button region. */
  101.     XColor *selectorFg;        /* Color for selector. */
  102.     GC selectorGC;        /* For drawing highlight when this button
  103.                  * is in selected state. */
  104.     int selectorSpace;        /* Horizontal space (in pixels) allocated for
  105.                  * display of selector. */
  106.     int selectorDiameter;    /* Diameter of selector, in pixels. */
  107.  
  108.     /*
  109.      * For check and radio buttons, the fields below are used
  110.      * to manage the variable indicating the button's state.
  111.      */
  112.  
  113.     char *selVarName;        /* Name of variable used to control selected
  114.                  * state of button.  Malloc'ed (if
  115.                  * not NULL). */
  116.     char *onValue;        /* Value to store in variable when
  117.                  * this button is selected.  Malloc'ed (if
  118.                  * not NULL). */
  119.     char *offValue;        /* Value to store in variable when this
  120.                  * button isn't selected.  Malloc'ed
  121.                  * (if not NULL).  Valid only for check
  122.                  * buttons. */
  123.  
  124.     /*
  125.      * Miscellaneous information:
  126.      */
  127.  
  128.     Cursor cursor;        /* Current cursor for window, or None. */
  129.     char *command;        /* Command to execute when button is
  130.                  * invoked; valid for buttons only.
  131.                  * If not NULL, it's malloc-ed. */
  132.     int flags;            /* Various flags;  see below for
  133.                  * definitions. */
  134. } Button;
  135.  
  136. /*
  137.  * Possible "type" values for buttons.  These are the kinds of
  138.  * widgets supported by this file.  The ordering of the type
  139.  * numbers is significant:  greater means more features and is
  140.  * used in the code.
  141.  */
  142.  
  143. #define TYPE_LABEL        0
  144. #define TYPE_BUTTON        1
  145. #define TYPE_CHECK_BUTTON    2
  146. #define TYPE_RADIO_BUTTON    3
  147.  
  148. /*
  149.  * Class names for buttons, indexed by one of the type values above.
  150.  */
  151.  
  152. static char *classNames[] = {"Label", "Button", "CheckButton", "RadioButton"};
  153.  
  154. /*
  155.  * Flag bits for buttons:
  156.  *
  157.  * REDRAW_PENDING:        Non-zero means a DoWhenIdle handler
  158.  *                has already been queued to redraw
  159.  *                this window.
  160.  * SELECTED:            Non-zero means this button is selected,
  161.  *                so special highlight should be drawn.
  162.  */
  163.  
  164. #define REDRAW_PENDING        1
  165. #define SELECTED        2
  166.  
  167. /*
  168.  * Mask values used to selectively enable entries in the
  169.  * configuration specs:
  170.  */
  171.  
  172. #define LABEL_MASK        TK_CONFIG_USER_BIT
  173. #define BUTTON_MASK        TK_CONFIG_USER_BIT << 1
  174. #define CHECK_BUTTON_MASK    TK_CONFIG_USER_BIT << 2
  175. #define RADIO_BUTTON_MASK    TK_CONFIG_USER_BIT << 3
  176. #define ALL_MASK        (LABEL_MASK | BUTTON_MASK \
  177.     | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK)
  178.  
  179. static int configFlags[] = {LABEL_MASK, BUTTON_MASK,
  180.     CHECK_BUTTON_MASK, RADIO_BUTTON_MASK};
  181. /*
  182.  * Information used for parsing configuration specs:
  183.  */
  184.  
  185. static Tk_ConfigSpec configSpecs[] = {
  186.     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
  187.     DEF_BUTTON_ACTIVE_BG_COLOR, Tk_Offset(Button, activeBorder),
  188.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
  189.     |TK_CONFIG_COLOR_ONLY},
  190.     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
  191.     DEF_BUTTON_ACTIVE_BG_MONO, Tk_Offset(Button, activeBorder),
  192.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
  193.     |TK_CONFIG_MONO_ONLY},
  194.     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
  195.     DEF_BUTTON_ACTIVE_FG_COLOR, Tk_Offset(Button, activeFg), 
  196.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
  197.     |TK_CONFIG_COLOR_ONLY},
  198.     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
  199.     DEF_BUTTON_ACTIVE_FG_MONO, Tk_Offset(Button, activeFg), 
  200.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
  201.     |TK_CONFIG_MONO_ONLY},
  202.     {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
  203.     DEF_BUTTON_ANCHOR, Tk_Offset(Button, anchor), ALL_MASK},
  204.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  205.     DEF_BUTTON_BG_COLOR, Tk_Offset(Button, normalBorder),
  206.     ALL_MASK | TK_CONFIG_COLOR_ONLY},
  207.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  208.     DEF_BUTTON_BG_MONO, Tk_Offset(Button, normalBorder),
  209.     ALL_MASK | TK_CONFIG_MONO_ONLY},
  210.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  211.     (char *) NULL, 0, ALL_MASK},
  212.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  213.     (char *) NULL, 0, ALL_MASK},
  214.     {TK_CONFIG_BITMAP, "-bitmap", "bitmap", "Bitmap",
  215.     DEF_BUTTON_BITMAP, Tk_Offset(Button, bitmap),
  216.     ALL_MASK|TK_CONFIG_NULL_OK},
  217.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  218.     DEF_BUTTON_BORDER_WIDTH, Tk_Offset(Button, borderWidth), ALL_MASK},
  219.     {TK_CONFIG_STRING, "-command", "command", "Command",
  220.     DEF_BUTTON_COMMAND, Tk_Offset(Button, command),
  221.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  222.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  223.     DEF_BUTTON_CURSOR, Tk_Offset(Button, cursor),
  224.     ALL_MASK|TK_CONFIG_NULL_OK},
  225.     {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
  226.     "DisabledForeground", DEF_BUTTON_DISABLED_FG_COLOR,
  227.     Tk_Offset(Button, disabledFg), BUTTON_MASK|CHECK_BUTTON_MASK
  228.     |RADIO_BUTTON_MASK|TK_CONFIG_COLOR_ONLY|TK_CONFIG_NULL_OK},
  229.     {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
  230.     "DisabledForeground", DEF_BUTTON_DISABLED_FG_MONO,
  231.     Tk_Offset(Button, disabledFg), BUTTON_MASK|CHECK_BUTTON_MASK
  232.     |RADIO_BUTTON_MASK|TK_CONFIG_MONO_ONLY|TK_CONFIG_NULL_OK},
  233.     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
  234.     (char *) NULL, 0, ALL_MASK},
  235.     {TK_CONFIG_FONT, "-font", "font", "Font",
  236.     DEF_BUTTON_FONT, Tk_Offset(Button, fontPtr),
  237.     ALL_MASK},
  238.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  239.     DEF_BUTTON_FG, Tk_Offset(Button, normalFg), ALL_MASK},
  240.     {TK_CONFIG_INT, "-height", "height", "Height",
  241.     DEF_BUTTON_HEIGHT, Tk_Offset(Button, height), ALL_MASK},
  242.     {TK_CONFIG_STRING, "-offvalue", "offValue", "Value",
  243.     DEF_BUTTON_OFF_VALUE, Tk_Offset(Button, offValue),
  244.     CHECK_BUTTON_MASK},
  245.     {TK_CONFIG_STRING, "-onvalue", "onValue", "Value",
  246.     DEF_BUTTON_ON_VALUE, Tk_Offset(Button, onValue),
  247.     CHECK_BUTTON_MASK},
  248.     {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
  249.     DEF_BUTTON_PADX, Tk_Offset(Button, padX), ALL_MASK},
  250.     {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
  251.     DEF_BUTTON_PADY, Tk_Offset(Button, padY), ALL_MASK},
  252.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  253.     DEF_BUTTON_RELIEF, Tk_Offset(Button, relief), 
  254.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  255.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  256.     DEF_LABEL_RELIEF, Tk_Offset(Button, relief), LABEL_MASK},
  257.     {TK_CONFIG_COLOR, "-selector", "selector", "Foreground",
  258.     DEF_BUTTON_SELECTOR_COLOR, Tk_Offset(Button, selectorFg),
  259.     CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_COLOR_ONLY
  260.     |TK_CONFIG_NULL_OK},
  261.     {TK_CONFIG_COLOR, "-selector", "selector", "Foreground",
  262.     DEF_BUTTON_SELECTOR_MONO, Tk_Offset(Button, selectorFg),
  263.     CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_MONO_ONLY
  264.     |TK_CONFIG_NULL_OK},
  265.     {TK_CONFIG_UID, "-state", "state", "State",
  266.     DEF_BUTTON_STATE, Tk_Offset(Button, state),
  267.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  268.     {TK_CONFIG_STRING, "-text", "text", "Text",
  269.     DEF_BUTTON_TEXT, Tk_Offset(Button, text), ALL_MASK},
  270.     {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
  271.     DEF_BUTTON_TEXT_VARIABLE, Tk_Offset(Button, textVarName),
  272.     ALL_MASK|TK_CONFIG_NULL_OK},
  273.     {TK_CONFIG_STRING, "-value", "value", "Value",
  274.     DEF_BUTTON_VALUE, Tk_Offset(Button, onValue),
  275.     RADIO_BUTTON_MASK},
  276.     {TK_CONFIG_STRING, "-variable", "variable", "Variable",
  277.     DEF_RADIOBUTTON_VARIABLE, Tk_Offset(Button, selVarName),
  278.     RADIO_BUTTON_MASK},
  279.     {TK_CONFIG_STRING, "-variable", "variable", "Variable",
  280.     DEF_CHECKBUTTON_VARIABLE, Tk_Offset(Button, selVarName),
  281.     CHECK_BUTTON_MASK},
  282.     {TK_CONFIG_INT, "-width", "width", "Width",
  283.     DEF_BUTTON_WIDTH, Tk_Offset(Button, width), ALL_MASK},
  284.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  285.     (char *) NULL, 0, 0}
  286. };
  287.  
  288. /*
  289.  * String to print out in error messages, identifying options for
  290.  * widget commands for different types of labels or buttons:
  291.  */
  292.  
  293. static char *optionStrings[] = {
  294.     "configure",
  295.     "activate, configure, deactivate, flash, or invoke",
  296.     "activate, configure, deactivate, deselect, flash, invoke, select, or toggle",
  297.     "activate, configure, deactivate, deselect, flash, invoke, or select"
  298. };
  299.  
  300. /*
  301.  * Forward declarations for procedures defined later in this file:
  302.  */
  303.  
  304. static void        ButtonEventProc _ANSI_ARGS_((ClientData clientData,
  305.                 XEvent *eventPtr));
  306. static char *        ButtonTextVarProc _ANSI_ARGS_((ClientData clientData,
  307.                 Tcl_Interp *interp, char *name1, char *name2,
  308.                 int flags));
  309. static char *        ButtonVarProc _ANSI_ARGS_((ClientData clientData,
  310.                 Tcl_Interp *interp, char *name1, char *name2,
  311.                 int flags));
  312. static int        ButtonWidgetCmd _ANSI_ARGS_((ClientData clientData,
  313.                 Tcl_Interp *interp, int argc, char **argv));
  314. static void        ComputeButtonGeometry _ANSI_ARGS_((Button *butPtr));
  315. static int        ConfigureButton _ANSI_ARGS_((Tcl_Interp *interp,
  316.                 Button *butPtr, int argc, char **argv,
  317.                 int flags));
  318. static void        DestroyButton _ANSI_ARGS_((ClientData clientData));
  319. static void        DisplayButton _ANSI_ARGS_((ClientData clientData));
  320. static int        InvokeButton  _ANSI_ARGS_((Button *butPtr));
  321.  
  322. /*
  323.  *--------------------------------------------------------------
  324.  *
  325.  * Tk_ButtonCmd --
  326.  *
  327.  *    This procedure is invoked to process the "button", "label",
  328.  *    "radiobutton", and "checkbutton" Tcl commands.  See the
  329.  *    user documentation for details on what it does.
  330.  *
  331.  * Results:
  332.  *    A standard Tcl result.
  333.  *
  334.  * Side effects:
  335.  *    See the user documentation.
  336.  *
  337.  *--------------------------------------------------------------
  338.  */
  339.  
  340. int
  341. Tk_ButtonCmd(clientData, interp, argc, argv)
  342.     ClientData clientData;    /* Main window associated with
  343.                  * interpreter. */
  344.     Tcl_Interp *interp;        /* Current interpreter. */
  345.     int argc;            /* Number of arguments. */
  346.     char **argv;        /* Argument strings. */
  347. {
  348.     register Button *butPtr;
  349.     int type;
  350.     Tk_Window tkwin = (Tk_Window) clientData;
  351.     Tk_Window new;
  352.  
  353.     if (argc < 2) {
  354.     Tcl_AppendResult(interp, "wrong # args:  should be \"",
  355.         argv[0], " pathName ?options?\"", (char *) NULL);
  356.     return TCL_ERROR;
  357.     }
  358.  
  359.     switch (argv[0][0]) {
  360.     case 'l':
  361.         type = TYPE_LABEL;
  362.         break;
  363.     case 'b':
  364.         type = TYPE_BUTTON;
  365.         break;
  366.     case 'c':
  367.         type = TYPE_CHECK_BUTTON;
  368.         break;
  369.     case 'r':
  370.         type = TYPE_RADIO_BUTTON;
  371.         break;
  372.     default:
  373.         sprintf(interp->result,
  374.             "unknown button-creation command \"%.50s\"");
  375.         return TCL_ERROR;
  376.     }
  377.  
  378.     /*
  379.      * Create the new window.
  380.      */
  381.  
  382.     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
  383.     if (new == NULL) {
  384.     return TCL_ERROR;
  385.     }
  386.  
  387.     /*
  388.      * Initialize the data structure for the button.
  389.      */
  390.  
  391.     butPtr = (Button *) ckalloc(sizeof(Button));
  392.     butPtr->tkwin = new;
  393.     butPtr->interp = interp;
  394.     butPtr->type = type;
  395.     butPtr->text = NULL;
  396.     butPtr->textVarName = NULL;
  397.     butPtr->bitmap = None;
  398.     butPtr->state = tkNormalUid;
  399.     butPtr->normalBorder = NULL;
  400.     butPtr->activeBorder = NULL;
  401.     butPtr->borderWidth = 0;
  402.     butPtr->relief = TK_RELIEF_FLAT;
  403.     butPtr->fontPtr = NULL;
  404.     butPtr->normalFg = NULL;
  405.     butPtr->activeFg = NULL;
  406.     butPtr->disabledFg = NULL;
  407.     butPtr->normalTextGC = None;
  408.     butPtr->activeTextGC = None;
  409.     butPtr->gray = None;
  410.     butPtr->disabledGC = None;
  411.     butPtr->selectorFg = NULL;
  412.     butPtr->selectorGC = None;
  413.     butPtr->selVarName = NULL;
  414.     butPtr->onValue = NULL;
  415.     butPtr->offValue = NULL;
  416.     butPtr->cursor = None;
  417.     butPtr->command = NULL;
  418.     butPtr->flags = 0;
  419.  
  420.     Tk_SetClass(new, classNames[type]);
  421.     Tk_CreateEventHandler(butPtr->tkwin, ExposureMask|StructureNotifyMask,
  422.         ButtonEventProc, (ClientData) butPtr);
  423.     Tcl_CreateCommand(interp, Tk_PathName(butPtr->tkwin), ButtonWidgetCmd,
  424.         (ClientData) butPtr, (void (*)()) NULL);
  425.     if (ConfigureButton(interp, butPtr, argc-2, argv+2,
  426.         configFlags[type]) != TCL_OK) {
  427.     Tk_DestroyWindow(butPtr->tkwin);
  428.     return TCL_ERROR;
  429.     }
  430.  
  431.     interp->result = Tk_PathName(butPtr->tkwin);
  432.     return TCL_OK;
  433. }
  434.  
  435. /*
  436.  *--------------------------------------------------------------
  437.  *
  438.  * ButtonWidgetCmd --
  439.  *
  440.  *    This procedure is invoked to process the Tcl command
  441.  *    that corresponds to a widget managed by this module.
  442.  *    See the user documentation for details on what it does.
  443.  *
  444.  * Results:
  445.  *    A standard Tcl result.
  446.  *
  447.  * Side effects:
  448.  *    See the user documentation.
  449.  *
  450.  *--------------------------------------------------------------
  451.  */
  452.  
  453. static int
  454. ButtonWidgetCmd(clientData, interp, argc, argv)
  455.     ClientData clientData;    /* Information about button widget. */
  456.     Tcl_Interp *interp;        /* Current interpreter. */
  457.     int argc;            /* Number of arguments. */
  458.     char **argv;        /* Argument strings. */
  459. {
  460.     register Button *butPtr = (Button *) clientData;
  461.     int result = TCL_OK;
  462.     int length;
  463.     char c;
  464.  
  465.     if (argc < 2) {
  466.     sprintf(interp->result,
  467.         "wrong # args: should be \"%.50s option [arg arg ...]\"",
  468.         argv[0]);
  469.     return TCL_ERROR;
  470.     }
  471.     Tk_Preserve((ClientData) butPtr);
  472.     c = argv[1][0];
  473.     length = strlen(argv[1]);
  474.     if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0)
  475.         && (butPtr->type != TYPE_LABEL)) {
  476.     if (argc > 2) {
  477.         sprintf(interp->result,
  478.             "wrong # args: should be \"%.50s activate\"",
  479.             argv[0]);
  480.         goto error;
  481.     }
  482.     if (butPtr->state != tkDisabledUid) {
  483.         butPtr->state = tkActiveUid;
  484.         Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->activeBorder);
  485.         goto redisplay;
  486.     }
  487.     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
  488.     if (argc == 2) {
  489.         result = Tk_ConfigureInfo(interp, butPtr->tkwin, configSpecs,
  490.             (char *) butPtr, (char *) NULL, configFlags[butPtr->type]);
  491.     } else if (argc == 3) {
  492.         result = Tk_ConfigureInfo(interp, butPtr->tkwin, configSpecs,
  493.             (char *) butPtr, argv[2],
  494.             configFlags[butPtr->type]);
  495.     } else {
  496.         result = ConfigureButton(interp, butPtr, argc-2, argv+2,
  497.             configFlags[butPtr->type] | TK_CONFIG_ARGV_ONLY);
  498.     }
  499.     } else if ((c == 'd') && (strncmp(argv[1], "deactivate", length) == 0)
  500.         && (length > 2) && (butPtr->type != TYPE_LABEL)) {
  501.     if (argc > 2) {
  502.         sprintf(interp->result,
  503.             "wrong # args: should be \"%.50s deactivate\"",
  504.             argv[0]);
  505.         goto error;
  506.     }
  507.     if (butPtr->state != tkDisabledUid) {
  508.         butPtr->state = tkNormalUid;
  509.         Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->normalBorder);
  510.         goto redisplay;
  511.     }
  512.     } else if ((c == 'd') && (strncmp(argv[1], "deselect", length) == 0)
  513.         && (length > 2) && (butPtr->type >= TYPE_CHECK_BUTTON)) {
  514.     if (argc > 2) {
  515.         sprintf(interp->result,
  516.             "wrong # args: should be \"%.50s deselect\"",
  517.             argv[0]);
  518.         goto error;
  519.     }
  520.     if (butPtr->type == TYPE_CHECK_BUTTON) {
  521.         Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue,
  522.             TCL_GLOBAL_ONLY);
  523.     } else if (butPtr->flags & SELECTED) {
  524.         Tcl_SetVar(interp, butPtr->selVarName, "", TCL_GLOBAL_ONLY);
  525.     }
  526.     } else if ((c == 'f') && (strncmp(argv[1], "flash", length) == 0)
  527.     && (butPtr->type != TYPE_LABEL)) {
  528.     int i;
  529.  
  530.     if (argc > 2) {
  531.         sprintf(interp->result,
  532.             "wrong # args: should be \"%.50s flash\"",
  533.             argv[0]);
  534.         goto error;
  535.     }
  536.     if (butPtr->state != tkDisabledUid) {
  537.         for (i = 0; i < 4; i++) {
  538.         butPtr->state = (butPtr->state == tkNormalUid)
  539.             ? tkActiveUid : tkNormalUid;
  540.         Tk_SetBackgroundFromBorder(butPtr->tkwin,
  541.             (butPtr->state == tkActiveUid) ? butPtr->activeBorder
  542.             : butPtr->normalBorder);
  543.         DisplayButton((ClientData) butPtr);
  544.         XFlush(Tk_Display(butPtr->tkwin));
  545.         Tk_Sleep(50);
  546.         }
  547.     }
  548.     } else if ((c == 'i') && (strncmp(argv[1], "invoke", length) == 0)
  549.         && (butPtr->type > TYPE_LABEL)) {
  550.     if (argc > 2) {
  551.         sprintf(interp->result,
  552.             "wrong # args: should be \"%.50s invoke\"",
  553.             argv[0]);
  554.         goto error;
  555.     }
  556.     if (butPtr->state != tkDisabledUid) {
  557.         result = InvokeButton(butPtr);
  558.     }
  559.     } else if ((c == 's') && (strncmp(argv[1], "select", length) == 0)
  560.         && (butPtr->type >= TYPE_CHECK_BUTTON)) {
  561.     if (argc > 2) {
  562.         sprintf(interp->result,
  563.             "wrong # args: should be \"%.50s select\"",
  564.             argv[0]);
  565.         goto error;
  566.     }
  567.     Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue, TCL_GLOBAL_ONLY);
  568.     } else if ((c == 't') && (strncmp(argv[1], "toggle", length) == 0)
  569.         && (length >= 2) && (butPtr->type == TYPE_CHECK_BUTTON)) {
  570.     if (argc > 2) {
  571.         sprintf(interp->result,
  572.             "wrong # args: should be \"%.50s select\"",
  573.             argv[0]);
  574.         goto error;
  575.     }
  576.     if (butPtr->flags & SELECTED) {
  577.         Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue, TCL_GLOBAL_ONLY);
  578.     } else {
  579.         Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue, TCL_GLOBAL_ONLY);
  580.     }
  581.     } else {
  582.     sprintf(interp->result,
  583.         "bad option \"%.50s\":  must be %s", argv[1],
  584.         optionStrings[butPtr->type]);
  585.     goto error;
  586.     }
  587.     Tk_Release((ClientData) butPtr);
  588.     return result;
  589.  
  590.     redisplay:
  591.     if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
  592.     Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
  593.     butPtr->flags |= REDRAW_PENDING;
  594.     }
  595.     Tk_Release((ClientData) butPtr);
  596.     return TCL_OK;
  597.  
  598.     error:
  599.     Tk_Release((ClientData) butPtr);
  600.     return TCL_ERROR;
  601. }
  602.  
  603. /*
  604.  *----------------------------------------------------------------------
  605.  *
  606.  * DestroyButton --
  607.  *
  608.  *    This procedure is invoked by Tk_EventuallyFree or Tk_Release
  609.  *    to clean up the internal structure of a button at a safe time
  610.  *    (when no-one is using it anymore).
  611.  *
  612.  * Results:
  613.  *    None.
  614.  *
  615.  * Side effects:
  616.  *    Everything associated with the widget is freed up.
  617.  *
  618.  *----------------------------------------------------------------------
  619.  */
  620.  
  621. static void
  622. DestroyButton(clientData)
  623.     ClientData clientData;        /* Info about entry widget. */
  624. {
  625.     register Button *butPtr = (Button *) clientData;
  626.  
  627.     if (butPtr->text != NULL) {
  628.     ckfree(butPtr->text);
  629.     }
  630.     if (butPtr->textVarName != NULL) {
  631.     Tcl_UntraceVar(butPtr->interp, butPtr->textVarName,
  632.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  633.         ButtonTextVarProc, (ClientData) butPtr);
  634.     ckfree(butPtr->textVarName);
  635.     }
  636.     if (butPtr->bitmap != None) {
  637.     Tk_FreeBitmap(butPtr->bitmap);
  638.     }
  639.     if (butPtr->normalBorder != NULL) {
  640.     Tk_Free3DBorder(butPtr->normalBorder);
  641.     }
  642.     if (butPtr->activeBorder != NULL) {
  643.     Tk_Free3DBorder(butPtr->activeBorder);
  644.     }
  645.     if (butPtr->fontPtr != NULL) {
  646.     Tk_FreeFontStruct(butPtr->fontPtr);
  647.     }
  648.     if (butPtr->normalFg != NULL) {
  649.     Tk_FreeColor(butPtr->normalFg);
  650.     }
  651.     if (butPtr->disabledFg != NULL) {
  652.     Tk_FreeColor(butPtr->disabledFg);
  653.     }
  654.     if (butPtr->activeFg != NULL) {
  655.     Tk_FreeColor(butPtr->activeFg);
  656.     }
  657.     if (butPtr->normalTextGC != None) {
  658.     Tk_FreeGC(butPtr->normalTextGC);
  659.     }
  660.     if (butPtr->activeTextGC != None) {
  661.     Tk_FreeGC(butPtr->activeTextGC);
  662.     }
  663.     if (butPtr->gray != None) {
  664.     Tk_FreeBitmap(butPtr->gray);
  665.     }
  666.     if (butPtr->disabledGC != None) {
  667.     Tk_FreeGC(butPtr->disabledGC);
  668.     }
  669.     if (butPtr->selectorFg != NULL) {
  670.     Tk_FreeColor(butPtr->selectorFg);
  671.     }
  672.     if (butPtr->selectorGC != None) {
  673.     Tk_FreeGC(butPtr->selectorGC);
  674.     }
  675.     if (butPtr->selVarName != NULL) {
  676.     Tcl_UntraceVar(butPtr->interp, butPtr->selVarName,
  677.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  678.         ButtonVarProc, (ClientData) butPtr);
  679.     ckfree(butPtr->selVarName);
  680.     }
  681.     if (butPtr->onValue != NULL) {
  682.     ckfree(butPtr->onValue);
  683.     }
  684.     if (butPtr->offValue != NULL) {
  685.     ckfree(butPtr->offValue);
  686.     }
  687.     if (butPtr->cursor != None) {
  688.     Tk_FreeCursor(butPtr->cursor);
  689.     }
  690.     if (butPtr->command != NULL) {
  691.     ckfree(butPtr->command);
  692.     }
  693.     ckfree((char *) butPtr);
  694. }
  695.  
  696. /*
  697.  *----------------------------------------------------------------------
  698.  *
  699.  * ConfigureButton --
  700.  *
  701.  *    This procedure is called to process an argv/argc list, plus
  702.  *    the Tk option database, in order to configure (or
  703.  *    reconfigure) a button widget.
  704.  *
  705.  * Results:
  706.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  707.  *    returned, then interp->result contains an error message.
  708.  *
  709.  * Side effects:
  710.  *    Configuration information, such as text string, colors, font,
  711.  *    etc. get set for butPtr;  old resources get freed, if there
  712.  *    were any.  The button is redisplayed.
  713.  *
  714.  *----------------------------------------------------------------------
  715.  */
  716.  
  717. static int
  718. ConfigureButton(interp, butPtr, argc, argv, flags)
  719.     Tcl_Interp *interp;        /* Used for error reporting. */
  720.     register Button *butPtr;    /* Information about widget;  may or may
  721.                  * not already have values for some fields. */
  722.     int argc;            /* Number of valid entries in argv. */
  723.     char **argv;        /* Arguments. */
  724.     int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  725. {
  726.     XGCValues gcValues;
  727.     GC newGC;
  728.     unsigned long mask;
  729.  
  730.     /*
  731.      * Eliminate any existing trace on variables monitored by the button.
  732.      */
  733.  
  734.     if (butPtr->textVarName != NULL) {
  735.     Tcl_UntraceVar(interp, butPtr->textVarName, 
  736.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  737.         ButtonTextVarProc, (ClientData) butPtr);
  738.     }
  739.     if (butPtr->selVarName != NULL) {
  740.     Tcl_UntraceVar(interp, butPtr->selVarName, 
  741.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  742.         ButtonVarProc, (ClientData) butPtr);
  743.     }
  744.  
  745.     if (Tk_ConfigureWidget(interp, butPtr->tkwin, configSpecs,
  746.         argc, argv, (char *) butPtr, flags) != TCL_OK) {
  747.     return TCL_ERROR;
  748.     }
  749.  
  750.     /*
  751.      * A few options need special processing, such as setting the
  752.      * background from a 3-D border, or filling in complicated
  753.      * defaults that couldn't be specified to Tk_ConfigureWidget.
  754.      */
  755.  
  756.     if (butPtr->state == tkActiveUid) {
  757.     Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->activeBorder);
  758.     } else {
  759.     Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->normalBorder);
  760.     if ((butPtr->state != tkNormalUid)
  761.         && (butPtr->state != tkDisabledUid)) {
  762.         Tcl_AppendResult(interp, "bad state value \"", butPtr->state,
  763.             "\":  must be normal, active, or disabled", (char *) NULL);
  764.         butPtr->state = tkNormalUid;
  765.         return TCL_ERROR;
  766.     }
  767.     }
  768.  
  769.     gcValues.font = butPtr->fontPtr->fid;
  770.     gcValues.foreground = butPtr->normalFg->pixel;
  771.     gcValues.background = Tk_3DBorderColor(butPtr->normalBorder)->pixel;
  772.  
  773.     /*
  774.      * Note: GraphicsExpose events are disabled in normalTextGC because it's
  775.      * used to copy stuff from an off-screen pixmap onto the screen (we know
  776.      * that there's no problem with obscured areas).
  777.      */
  778.  
  779.     gcValues.graphics_exposures = False;
  780.     newGC = Tk_GetGC(butPtr->tkwin,
  781.         GCForeground|GCBackground|GCFont|GCGraphicsExposures, &gcValues);
  782.     if (butPtr->normalTextGC != None) {
  783.     Tk_FreeGC(butPtr->normalTextGC);
  784.     }
  785.     butPtr->normalTextGC = newGC;
  786.  
  787.     if (butPtr->activeFg != NULL) {
  788.     gcValues.font = butPtr->fontPtr->fid;
  789.     gcValues.foreground = butPtr->activeFg->pixel;
  790.     gcValues.background = Tk_3DBorderColor(butPtr->activeBorder)->pixel;
  791.     newGC = Tk_GetGC(butPtr->tkwin, GCForeground|GCBackground|GCFont,
  792.         &gcValues);
  793.     if (butPtr->activeTextGC != None) {
  794.         Tk_FreeGC(butPtr->activeTextGC);
  795.     }
  796.     butPtr->activeTextGC = newGC;
  797.     }
  798.  
  799.     gcValues.font = butPtr->fontPtr->fid;
  800.     gcValues.background = Tk_3DBorderColor(butPtr->normalBorder)->pixel;
  801.     if (butPtr->disabledFg != NULL) {
  802.     gcValues.foreground = butPtr->disabledFg->pixel;
  803.     mask = GCForeground|GCBackground|GCFont;
  804.     } else {
  805.     gcValues.foreground = gcValues.background;
  806.     if (butPtr->gray == None) {
  807.         butPtr->gray = Tk_GetBitmap(interp, butPtr->tkwin,
  808.             Tk_GetUid("gray50"));
  809.         if (butPtr->gray == None) {
  810.         return TCL_ERROR;
  811.         }
  812.     }
  813.     gcValues.fill_style = FillStippled;
  814.     gcValues.stipple = butPtr->gray;
  815.     mask = GCForeground|GCFillStyle|GCStipple;
  816.     }
  817.     newGC = Tk_GetGC(butPtr->tkwin, mask, &gcValues);
  818.     if (butPtr->disabledGC != None) {
  819.     Tk_FreeGC(butPtr->disabledGC);
  820.     }
  821.     butPtr->disabledGC = newGC;
  822.  
  823.     if (butPtr->padX < 0) {
  824.     butPtr->padX = 0;
  825.     }
  826.     if (butPtr->padY < 0) {
  827.     butPtr->padY = 0;
  828.     }
  829.  
  830.     if (butPtr->type >= TYPE_CHECK_BUTTON) {
  831.     char *value;
  832.  
  833.     if (butPtr->selectorFg != NULL) {
  834.         gcValues.foreground = butPtr->selectorFg->pixel;
  835.         newGC = Tk_GetGC(butPtr->tkwin, GCForeground, &gcValues);
  836.     } else {
  837.         newGC = None;
  838.     }
  839.     if (butPtr->selectorGC != None) {
  840.         Tk_FreeGC(butPtr->selectorGC);
  841.     }
  842.     butPtr->selectorGC = newGC;
  843.  
  844.     if (butPtr->selVarName == NULL) {
  845.         butPtr->selVarName = (char *) ckalloc((unsigned)
  846.             (strlen(Tk_Name(butPtr->tkwin)) + 1));
  847.         strcpy(butPtr->selVarName, Tk_Name(butPtr->tkwin));
  848.     }
  849.     if (butPtr->onValue == NULL) {
  850.         butPtr->onValue = (char *) ckalloc((unsigned)
  851.             (strlen(Tk_Name(butPtr->tkwin)) + 1));
  852.         strcpy(butPtr->onValue, Tk_Name(butPtr->tkwin));
  853.     }
  854.  
  855.     /*
  856.      * Select the button if the associated variable has the
  857.      * appropriate value, initialize the variable if it doesn't
  858.      * exist, then set a trace on the variable to monitor future
  859.      * changes to its value.
  860.      */
  861.  
  862.     value = Tcl_GetVar(interp, butPtr->selVarName, TCL_GLOBAL_ONLY);
  863.     butPtr->flags &= ~SELECTED;
  864.     if (value != NULL) {
  865.         if (strcmp(value, butPtr->onValue) == 0) {
  866.         butPtr->flags |= SELECTED;
  867.         }
  868.     } else {
  869.         Tcl_SetVar(interp, butPtr->selVarName,
  870.             (butPtr->type == TYPE_CHECK_BUTTON) ? butPtr->offValue : "",
  871.             TCL_GLOBAL_ONLY);
  872.     }
  873.     Tcl_TraceVar(interp, butPtr->selVarName,
  874.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  875.         ButtonVarProc, (ClientData) butPtr);
  876.     }
  877.  
  878.     /*
  879.      * If the button is to display the value of a variable, then set up
  880.      * a trace on the variable's value, create the variable if it doesn't
  881.      * exist, and fetch its current value.
  882.      */
  883.  
  884.     if ((butPtr->bitmap == None) && (butPtr->textVarName != NULL)) {
  885.     char *value;
  886.  
  887.     value = Tcl_GetVar(interp, butPtr->textVarName, TCL_GLOBAL_ONLY);
  888.     if (value == NULL) {
  889.         Tcl_SetVar(interp, butPtr->textVarName, butPtr->text,
  890.             TCL_GLOBAL_ONLY);
  891.     } else {
  892.         if (butPtr->text != NULL) {
  893.         ckfree(butPtr->text);
  894.         }
  895.         butPtr->text = ckalloc((unsigned) (strlen(value) + 1));
  896.         strcpy(butPtr->text, value);
  897.     }
  898.     Tcl_TraceVar(interp, butPtr->textVarName,
  899.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  900.         ButtonTextVarProc, (ClientData) butPtr);
  901.     }
  902.  
  903.     ComputeButtonGeometry(butPtr);
  904.  
  905.     /*
  906.      * Lastly, arrange for the button to be redisplayed.
  907.      */
  908.  
  909.     if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
  910.     Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
  911.     butPtr->flags |= REDRAW_PENDING;
  912.     }
  913.  
  914.     return TCL_OK;
  915. }
  916.  
  917. /*
  918.  *----------------------------------------------------------------------
  919.  *
  920.  * DisplayButton --
  921.  *
  922.  *    This procedure is invoked to display a button widget.
  923.  *
  924.  * Results:
  925.  *    None.
  926.  *
  927.  * Side effects:
  928.  *    Commands are output to X to display the button in its
  929.  *    current mode.
  930.  *
  931.  *----------------------------------------------------------------------
  932.  */
  933.  
  934. static void
  935. DisplayButton(clientData)
  936.     ClientData clientData;    /* Information about widget. */
  937. {
  938.     register Button *butPtr = (Button *) clientData;
  939.     GC gc;
  940.     Tk_3DBorder border;
  941.     Pixmap pixmap;
  942.     int x = 0;            /* Initialization only needed to stop
  943.                  * compiler warning. */
  944.     int y;
  945.     register Tk_Window tkwin = butPtr->tkwin;
  946.  
  947.     butPtr->flags &= ~REDRAW_PENDING;
  948.     if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  949.     return;
  950.     }
  951.  
  952.     if ((butPtr->state == tkDisabledUid) && (butPtr->disabledFg != NULL)) {
  953.     gc = butPtr->disabledGC;
  954.     border = butPtr->normalBorder;
  955.     } else if (butPtr->state == tkActiveUid) {
  956.     gc = butPtr->activeTextGC;
  957.     border = butPtr->activeBorder;
  958.     } else {
  959.     gc = butPtr->normalTextGC;
  960.     border = butPtr->normalBorder;
  961.     }
  962.  
  963.     /*
  964.      * In order to avoid screen flashes, this procedure redraws
  965.      * the button in a pixmap, then copies the pixmap to the
  966.      * screen in a single operation.  This means that there's no
  967.      * point in time where the on-sreen image has been cleared.
  968.      */
  969.  
  970.     pixmap = XCreatePixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
  971.         Tk_Width(tkwin), Tk_Height(tkwin),
  972.         DefaultDepthOfScreen(Tk_Screen(tkwin)));
  973.     Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap, border,
  974.         0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
  975.  
  976.     /*
  977.      * Display bitmap or text for button.
  978.      */
  979.  
  980.     if (butPtr->bitmap != None) {
  981.     unsigned int width, height;
  982.  
  983.     Tk_SizeOfBitmap(butPtr->bitmap, &width, &height);
  984.     switch (butPtr->anchor) {
  985.         case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
  986.         x = butPtr->borderWidth + butPtr->selectorSpace
  987.             + butPtr->padX + 1;
  988.         break;
  989.         case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
  990.         x = (Tk_Width(tkwin) + butPtr->selectorSpace - width)/2;
  991.         break;
  992.         default:
  993.         x = Tk_Width(tkwin) - butPtr->borderWidth - butPtr->padX
  994.             - width - 1;
  995.         break;
  996.     }
  997.     switch (butPtr->anchor) {
  998.         case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
  999.         y = butPtr->borderWidth + butPtr->padY + 1;
  1000.         break;
  1001.         case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
  1002.         y = (Tk_Height(tkwin) - height)/2;
  1003.         break;
  1004.         default:
  1005.         y = Tk_Height(tkwin) - butPtr->borderWidth - butPtr->padY
  1006.             - height - 1;
  1007.         break;
  1008.     }
  1009.     if (butPtr->relief == TK_RELIEF_RAISED) {
  1010.         x -= 1;
  1011.         y -= 1;
  1012.     } else if (butPtr->relief == TK_RELIEF_SUNKEN) {
  1013.         x += 1;
  1014.         y += 1;
  1015.     }
  1016.     XCopyPlane(Tk_Display(tkwin), butPtr->bitmap, pixmap,
  1017.         gc, 0, 0, width, height, x, y, 1);
  1018.     y += height/2;
  1019.     } else {
  1020.     switch (butPtr->anchor) {
  1021.         case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
  1022.         x = butPtr->borderWidth + butPtr->padX + butPtr->selectorSpace
  1023.             + butPtr->leftBearing + 1;
  1024.         break;
  1025.         case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
  1026.         x = (Tk_Width(tkwin) + butPtr->selectorSpace
  1027.             + butPtr->leftBearing - butPtr->rightBearing)/2;
  1028.         break;
  1029.         default:
  1030.         x = Tk_Width(tkwin) - butPtr->borderWidth - butPtr->padX
  1031.             - butPtr->rightBearing - 1;
  1032.         break;
  1033.     }
  1034.     switch (butPtr->anchor) {
  1035.         case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
  1036.         y = butPtr->borderWidth + butPtr->fontPtr->ascent
  1037.             + butPtr->padY + 1;
  1038.         break;
  1039.         case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
  1040.         y = (Tk_Height(tkwin) + butPtr->fontPtr->ascent
  1041.             - butPtr->fontPtr->descent)/2;
  1042.         break;
  1043.         default:
  1044.         y = Tk_Height(tkwin) - butPtr->borderWidth - butPtr->padY
  1045.             - butPtr->fontPtr->descent - 1;
  1046.         break;
  1047.     }
  1048.     if (butPtr->relief == TK_RELIEF_RAISED) {
  1049.         x -= 1;
  1050.         y -= 1;
  1051.     } else if (butPtr->relief == TK_RELIEF_SUNKEN) {
  1052.         x += 1;
  1053.         y += 1;
  1054.     }
  1055.     XDrawString(Tk_Display(tkwin), pixmap, gc, x, y,
  1056.         butPtr->text, butPtr->textLength);
  1057.     y -= (butPtr->fontPtr->ascent - butPtr->fontPtr->descent)/2;
  1058.     x -= butPtr->leftBearing;
  1059.     }
  1060.  
  1061.     /*
  1062.      * Draw the selector for check buttons and radio buttons.  At this
  1063.      * point x and y refer to the top-left corner of the text or bitmap.
  1064.      */
  1065.  
  1066.     if ((butPtr->type == TYPE_CHECK_BUTTON) && (butPtr->selectorGC != None)) {
  1067.     int dim;
  1068.  
  1069.     dim = butPtr->selectorDiameter;
  1070.     x -= (butPtr->selectorSpace + butPtr->padX + dim)/2;
  1071.     y -= dim/2;
  1072.     Tk_Draw3DRectangle(Tk_Display(tkwin), pixmap, border, x, y,
  1073.         dim, dim, butPtr->borderWidth, TK_RELIEF_SUNKEN);
  1074.     x += butPtr->borderWidth;
  1075.     y += butPtr->borderWidth;
  1076.     dim -= 2*butPtr->borderWidth;
  1077.     if (dim > 0) {
  1078.         if (butPtr->flags & SELECTED) {
  1079.         XFillRectangle(Tk_Display(tkwin), pixmap, butPtr->selectorGC,
  1080.             x, y, (unsigned int) dim, (unsigned int) dim);
  1081.         } else {
  1082.         Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap,
  1083.             butPtr->normalBorder, x, y, dim, dim,
  1084.             butPtr->borderWidth, TK_RELIEF_FLAT);
  1085.         }
  1086.     }
  1087.     } else if ((butPtr->type == TYPE_RADIO_BUTTON)
  1088.         && (butPtr->selectorGC != None)) {
  1089.     XPoint points[4];
  1090.     int radius;
  1091.  
  1092.     radius = butPtr->selectorDiameter/2;
  1093.     points[0].x = x - (butPtr->selectorSpace + butPtr->padX
  1094.         + butPtr->selectorDiameter)/2;
  1095.     points[0].y = y;
  1096.     points[1].x = points[0].x + radius;
  1097.     points[1].y = points[0].y + radius;
  1098.     points[2].x = points[1].x + radius;
  1099.     points[2].y = points[0].y;
  1100.     points[3].x = points[1].x;
  1101.     points[3].y = points[0].y - radius;
  1102.     if (butPtr->flags & SELECTED) {
  1103.         XFillPolygon(Tk_Display(tkwin), pixmap, butPtr->selectorGC,
  1104.             points, 4, Convex, CoordModeOrigin);
  1105.     } else {
  1106.         Tk_Fill3DPolygon(Tk_Display(tkwin), pixmap, butPtr->normalBorder,
  1107.             points, 4, butPtr->borderWidth, TK_RELIEF_FLAT);
  1108.     }
  1109.     Tk_Draw3DPolygon(Tk_Display(tkwin), pixmap, border,
  1110.         points, 4, butPtr->borderWidth, TK_RELIEF_RAISED);
  1111.     }
  1112.  
  1113.     /*
  1114.      * If the button is disabled with a stipple rather than a special
  1115.      * foreground color, generate the stippled effect.
  1116.      */
  1117.  
  1118.     if ((butPtr->state == tkDisabledUid) && (butPtr->disabledFg == NULL)) {
  1119.     XFillRectangle(Tk_Display(tkwin), pixmap, butPtr->disabledGC,
  1120.         butPtr->borderWidth, butPtr->borderWidth,
  1121.         (unsigned) (Tk_Width(tkwin) - 2*butPtr->borderWidth),
  1122.         (unsigned) (Tk_Height(tkwin) - 2*butPtr->borderWidth));
  1123.     }
  1124.  
  1125.     /*
  1126.      * Draw the border last.  This way, if the button's contents
  1127.      * overflow onto the border they'll be covered up by the border.
  1128.      */
  1129.  
  1130.     if (butPtr->relief != TK_RELIEF_FLAT) {
  1131.     Tk_Draw3DRectangle(Tk_Display(tkwin), pixmap, border,0, 0,
  1132.         Tk_Width(tkwin), Tk_Height(tkwin), butPtr->borderWidth,
  1133.         butPtr->relief);
  1134.     }
  1135.  
  1136.     /*
  1137.      * Copy the information from the off-screen pixmap onto the screen,
  1138.      * then delete the pixmap.
  1139.      */
  1140.  
  1141.     XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin),
  1142.     butPtr->normalTextGC, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, 0);
  1143.     XFreePixmap(Tk_Display(tkwin), pixmap);
  1144. }
  1145.  
  1146. /*
  1147.  *--------------------------------------------------------------
  1148.  *
  1149.  * ButtonEventProc --
  1150.  *
  1151.  *    This procedure is invoked by the Tk dispatcher for various
  1152.  *    events on buttons.
  1153.  *
  1154.  * Results:
  1155.  *    None.
  1156.  *
  1157.  * Side effects:
  1158.  *    When the window gets deleted, internal structures get
  1159.  *    cleaned up.  When it gets exposed, it is redisplayed.
  1160.  *
  1161.  *--------------------------------------------------------------
  1162.  */
  1163.  
  1164. static void
  1165. ButtonEventProc(clientData, eventPtr)
  1166.     ClientData clientData;    /* Information about window. */
  1167.     XEvent *eventPtr;        /* Information about event. */
  1168. {
  1169.     Button *butPtr = (Button *) clientData;
  1170.     if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
  1171.     if ((butPtr->tkwin != NULL) && !(butPtr->flags & REDRAW_PENDING)) {
  1172.         Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
  1173.         butPtr->flags |= REDRAW_PENDING;
  1174.     }
  1175.     } else if (eventPtr->type == DestroyNotify) {
  1176.     Tcl_DeleteCommand(butPtr->interp, Tk_PathName(butPtr->tkwin));
  1177.     butPtr->tkwin = NULL;
  1178.     if (butPtr->flags & REDRAW_PENDING) {
  1179.         Tk_CancelIdleCall(DisplayButton, (ClientData) butPtr);
  1180.     }
  1181.     Tk_EventuallyFree((ClientData) butPtr, DestroyButton);
  1182.     }
  1183. }
  1184.  
  1185. /*
  1186.  *----------------------------------------------------------------------
  1187.  *
  1188.  * ComputeButtonGeometry --
  1189.  *
  1190.  *    After changes in a button's text or bitmap, this procedure
  1191.  *    recomputes the button's geometry and passes this information
  1192.  *    along to the geometry manager for the window.
  1193.  *
  1194.  * Results:
  1195.  *    None.
  1196.  *
  1197.  * Side effects:
  1198.  *    The button's window may change size.
  1199.  *
  1200.  *----------------------------------------------------------------------
  1201.  */
  1202.  
  1203. static void
  1204. ComputeButtonGeometry(butPtr)
  1205.     register Button *butPtr;    /* Button whose geometry may have changed. */
  1206. {
  1207.     XCharStruct bbox;
  1208.     int dummy;
  1209.     unsigned int width, height;
  1210.  
  1211.     butPtr->selectorSpace = 0;
  1212.     if (butPtr->bitmap != None) {
  1213.     Tk_SizeOfBitmap(butPtr->bitmap, &width, &height);
  1214.     if (butPtr->width > 0) {
  1215.         width = butPtr->width;
  1216.     }
  1217.     if (butPtr->height > 0) {
  1218.         height = butPtr->height;
  1219.     }
  1220.     if ((butPtr->type >= TYPE_CHECK_BUTTON)
  1221.         && (butPtr->selectorGC != None)) {
  1222.         butPtr->selectorSpace = (14*height)/10;
  1223.         if (butPtr->type == TYPE_CHECK_BUTTON) {
  1224.         butPtr->selectorDiameter = (65*height)/100;
  1225.         } else {
  1226.         butPtr->selectorDiameter = (75*height)/100;
  1227.         }
  1228.     }
  1229.     } else {
  1230.     butPtr->textLength = strlen(butPtr->text);
  1231.     XTextExtents(butPtr->fontPtr, butPtr->text, butPtr->textLength,
  1232.         &dummy, &dummy, &dummy, &bbox);
  1233.     butPtr->leftBearing = bbox.lbearing;
  1234.     butPtr->rightBearing = bbox.rbearing;
  1235.     width = bbox.lbearing + bbox.rbearing;
  1236.     height = butPtr->fontPtr->ascent + butPtr->fontPtr->descent;
  1237.     if (butPtr->width > 0) {
  1238.         width = butPtr->width * XTextWidth(butPtr->fontPtr, "0", 1);
  1239.     }
  1240.     if (butPtr->height > 0) {
  1241.         height *= butPtr->height;
  1242.     }
  1243.     if ((butPtr->type >= TYPE_CHECK_BUTTON)
  1244.         && (butPtr->selectorGC != None)) {
  1245.         butPtr->selectorDiameter = butPtr->fontPtr->ascent
  1246.             + butPtr->fontPtr->descent;
  1247.         if (butPtr->type == TYPE_CHECK_BUTTON) {
  1248.         butPtr->selectorDiameter = (80*butPtr->selectorDiameter)/100;
  1249.         }
  1250.         butPtr->selectorSpace = butPtr->selectorDiameter + butPtr->padX;
  1251.     }
  1252.     }
  1253.  
  1254.     /*
  1255.      * When issuing the geometry request, add extra space for the selector,
  1256.      * if any, and for the border and padding, plus two extra pixels so the
  1257.      * display can be offset by 1 pixel in either direction for the raised
  1258.      * or lowered effect.
  1259.      */
  1260.  
  1261.     width += 2*butPtr->padX;
  1262.     height += 2*butPtr->padY;
  1263.     Tk_GeometryRequest(butPtr->tkwin, (int) (width + butPtr->selectorSpace
  1264.         + 2*butPtr->borderWidth + 2),
  1265.         (int) (height + 2*butPtr->borderWidth + 2));
  1266.     Tk_SetInternalBorder(butPtr->tkwin, butPtr->borderWidth);
  1267. }
  1268.  
  1269. /*
  1270.  *----------------------------------------------------------------------
  1271.  *
  1272.  * InvokeButton --
  1273.  *
  1274.  *    This procedure is called to carry out the actions associated
  1275.  *    with a button, such as invoking a Tcl command or setting a
  1276.  *    variable.  This procedure is invoked, for example, when the
  1277.  *    button is invoked via the mouse.
  1278.  *
  1279.  * Results:
  1280.  *    A standard Tcl return value.  Information is also left in
  1281.  *    interp->result.
  1282.  *
  1283.  * Side effects:
  1284.  *    Depends on the button and its associated command.
  1285.  *
  1286.  *----------------------------------------------------------------------
  1287.  */
  1288.  
  1289. static int
  1290. InvokeButton(butPtr)
  1291.     register Button *butPtr;        /* Information about button. */
  1292. {
  1293.     if (butPtr->type == TYPE_CHECK_BUTTON) {
  1294.     if (butPtr->flags & SELECTED) {
  1295.         Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->offValue,
  1296.             TCL_GLOBAL_ONLY);
  1297.     } else {
  1298.         Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
  1299.             TCL_GLOBAL_ONLY);
  1300.     }
  1301.     } else if (butPtr->type == TYPE_RADIO_BUTTON) {
  1302.     Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
  1303.         TCL_GLOBAL_ONLY);
  1304.     }
  1305.     if ((butPtr->type != TYPE_LABEL) && (butPtr->command != NULL)) {
  1306.     return Tcl_GlobalEval(butPtr->interp, butPtr->command);
  1307.     }
  1308.     return TCL_OK;
  1309. }
  1310.  
  1311. /*
  1312.  *--------------------------------------------------------------
  1313.  *
  1314.  * ButtonVarProc --
  1315.  *
  1316.  *    This procedure is invoked when someone changes the
  1317.  *    state variable associated with a radio button.  Depending
  1318.  *    on the new value of the button's variable, the button
  1319.  *    may be selected or deselected.
  1320.  *
  1321.  * Results:
  1322.  *    NULL is always returned.
  1323.  *
  1324.  * Side effects:
  1325.  *    The button may become selected or deselected.
  1326.  *
  1327.  *--------------------------------------------------------------
  1328.  */
  1329.  
  1330.     /* ARGSUSED */
  1331. static char *
  1332. ButtonVarProc(clientData, interp, name1, name2, flags)
  1333.     ClientData clientData;    /* Information about button. */
  1334.     Tcl_Interp *interp;        /* Interpreter containing variable. */
  1335.     char *name1;        /* Name of variable. */
  1336.     char *name2;        /* Second part of variable name. */
  1337.     int flags;            /* Information about what happened. */
  1338. {
  1339.     register Button *butPtr = (Button *) clientData;
  1340.     char *value;
  1341.  
  1342.     /*
  1343.      * If the variable is being unset, then just re-establish the
  1344.      * trace unless the whole interpreter is going away.
  1345.      */
  1346.  
  1347.     if (flags & TCL_TRACE_UNSETS) {
  1348.     butPtr->flags &= ~SELECTED;
  1349.     if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
  1350.         Tcl_TraceVar2(interp, name1, name2,
  1351.             TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  1352.             ButtonVarProc, clientData);
  1353.     }
  1354.     goto redisplay;
  1355.     }
  1356.  
  1357.     /*
  1358.      * Use the value of the variable to update the selected status of
  1359.      * the button.
  1360.      */
  1361.  
  1362.     value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY);
  1363.     if (strcmp(value, butPtr->onValue) == 0) {
  1364.     if (butPtr->flags & SELECTED) {
  1365.         return (char *) NULL;
  1366.     }
  1367.     butPtr->flags |= SELECTED;
  1368.     } else if (butPtr->flags & SELECTED) {
  1369.     butPtr->flags &= ~SELECTED;
  1370.     } else {
  1371.     return (char *) NULL;
  1372.     }
  1373.  
  1374.     redisplay:
  1375.     if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin)
  1376.         && !(butPtr->flags & REDRAW_PENDING)) {
  1377.     Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
  1378.     butPtr->flags |= REDRAW_PENDING;
  1379.     }
  1380.     return (char *) NULL;
  1381. }
  1382.  
  1383. /*
  1384.  *--------------------------------------------------------------
  1385.  *
  1386.  * ButtonTextVarProc --
  1387.  *
  1388.  *    This procedure is invoked when someone changes the variable
  1389.  *    whose contents are to be displayed in a button.
  1390.  *
  1391.  * Results:
  1392.  *    NULL is always returned.
  1393.  *
  1394.  * Side effects:
  1395.  *    The text displayed in the button will change to match the
  1396.  *    variable.
  1397.  *
  1398.  *--------------------------------------------------------------
  1399.  */
  1400.  
  1401.     /* ARGSUSED */
  1402. static char *
  1403. ButtonTextVarProc(clientData, interp, name1, name2, flags)
  1404.     ClientData clientData;    /* Information about button. */
  1405.     Tcl_Interp *interp;        /* Interpreter containing variable. */
  1406.     char *name1;        /* Name of variable. */
  1407.     char *name2;        /* Second part of variable name. */
  1408.     int flags;            /* Information about what happened. */
  1409. {
  1410.     register Button *butPtr = (Button *) clientData;
  1411.     char *value;
  1412.  
  1413.     /*
  1414.      * If the variable is unset, then immediately recreate it unless
  1415.      * the whole interpreter is going away.
  1416.      */
  1417.  
  1418.     if (flags & TCL_TRACE_UNSETS) {
  1419.     if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
  1420.         Tcl_SetVar2(interp, name1, name2, butPtr->text,
  1421.             flags & TCL_GLOBAL_ONLY);
  1422.         Tcl_TraceVar2(interp, name1, name2,
  1423.             TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  1424.             ButtonTextVarProc, clientData);
  1425.     }
  1426.     return (char *) NULL;
  1427.     }
  1428.  
  1429.     value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY);
  1430.     if (value == NULL) {
  1431.     value = "";
  1432.     }
  1433.     if (butPtr->text != NULL) {
  1434.     ckfree(butPtr->text);
  1435.     }
  1436.     butPtr->text = ckalloc((unsigned) (strlen(value) + 1));
  1437.     strcpy(butPtr->text, value);
  1438.     ComputeButtonGeometry(butPtr);
  1439.  
  1440.     if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin)
  1441.         && !(butPtr->flags & REDRAW_PENDING)) {
  1442.     Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
  1443.     butPtr->flags |= REDRAW_PENDING;
  1444.     }
  1445.     return (char *) NULL;
  1446. }
  1447.